{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "## 5.5 层次化索引 Hierarchical Indexing\r\n",
    "\r\n",
    "抽象地说，它提供了一种可以以较低维度的形式去处理较高纬度的数据的方法。（Somewhat abstractly, it provides a way for you to work with higher dimensional data in a lower dimensional form.）"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 模块导入\r\n",
    "import os, sys\r\n",
    "sys.path.append(os.path.dirname(os.getcwd()))\r\n",
    "import numpy\r\n",
    "import pandas\r\n",
    "from dependency import arr_info"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 绪  论\r\n",
    "\r\n",
    "+ `Series.unstack()`：将层次化索引的Series转换为DataFrame\r\n",
    "+ `DataFrame.stack()`：将DataFrame转换为层次化索引的Series\r\n",
    "\r\n",
    "索引名称（列表中名称的数量要和索引层次的数量一致）\r\n",
    "+ `object.index.names = [\"XXX\", \"XXX\"]`\r\n",
    "+ `DataFrame.columns.names = [\"XXX\", \"XXX\"]`\r\n",
    "\r\n",
    "注意：\r\n",
    "+ 层次化索引的名称是`names`，不是`name`；\r\n",
    "+ 不要混淆数据对象本身的名称：`object.name`；\r\n",
    "+ 不要混淆索引名称与行列标签（label）：`object.index`、`DataFrame.columns`；"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# Series\r\n",
    "\r\n",
    "## 创建具有层次化的索引\r\n",
    "ser0_1 = pandas.Series(numpy.arange(10), index=[[\"a\",\"a\",\"a\",\"b\",\"b\",\"b\",\"c\",\"c\",\"d\",\"d\"], [1,2,3,1,2,3,1,2,2,3]])\r\n",
    "\r\n",
    "arr_info([ser0_1])\r\n",
    "arr_info([ser0_1.index])    # 索引类型为：MultiIndex"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 选取数据\r\n",
    "arr_info([ ser0_1[\"a\"] ])\r\n",
    "arr_info([ ser0_1[\"a\", 3] ])        # 选取一个元素\r\n",
    "arr_info([ ser0_1[:, 2] ])           # 选取内层\r\n",
    "arr_info([ ser0_1[\"b\":\"c\"] ])       # 切片\r\n",
    "arr_info([ ser0_1[[\"a\", \"d\"]] ])    # 花式索引"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 更高维度数据的等价形式，以及数据的重塑、基于分组的操作（如透视表 Pivot Table）\r\n",
    "frame0_1 = pandas.DataFrame([[0,1,2],[3,4,5],[6,7,numpy.NaN],[numpy.NaN,8,9]],\r\n",
    "                            index=[\"a\",\"b\",\"c\",\"d\"], columns=[1,2,3])\r\n",
    "\r\n",
    "arr_info([ frame0_1 ])              # 等价的二维数据形式\r\n",
    "arr_info([ ser0_1.unstack() ])      # 将层次化索引的Series转换为DataFrame\r\n",
    "arr_info([ frame0_1.stack() ])      # 将DataFrame转换为层次化索引的Series"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 索引名称\r\n",
    "ser0_1.name = \"Value\"\r\n",
    "ser0_1.index.names = [\"Group\", \"NO.\"]\r\n",
    "arr_info([ ser0_1 ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# DataFrame\r\n",
    "\r\n",
    "## 创建具有层次化的索引\r\n",
    "frame0_1 = pandas.DataFrame(numpy.arange(18).reshape(6,3), \r\n",
    "                            index=[[\"A\",\"A\",\"A\",\"A\",\"B\",\"B\"],[1,2,3,4,1,2]], \r\n",
    "                            columns=[[\"item_1\",\"item_1\",\"item_2\"],[\"Red\",\"Green\",\"Blue\"]])\r\n",
    "frame0_1"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 选取数据\r\n",
    "arr_info([ frame0_1[\"item_1\"] ])\r\n",
    "arr_info([ frame0_1.loc[\"A\"] ])\r\n",
    "arr_info([ frame0_1.iloc[5] ])      # 整数索引仍然是0~(N-1)，并且是连续的\r\n",
    "arr_info([ frame0_1.iloc[3:6] ])    # 切片（针对行）"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 索引名称\r\n",
    "frame0_1.index.names = [\"Group\", \"NO.\"]\r\n",
    "frame0_1.columns.names = [\"Item\", \"Color\"]\r\n",
    "frame0_1"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "## 单独创建MultiIndex对象\r\n",
    "label = pandas.MultiIndex.from_arrays([[\"item_1\",\"item_1\",\"item_2\"],[\"Red\",\"Green\",\"Blue\"]],\r\n",
    "                                        names=[\"Item\", \"Color\"])\r\n",
    "\r\n",
    "frame0_2 = pandas.DataFrame(numpy.arange(9).reshape(3,3), index=label)\r\n",
    "frame0_3 = pandas.DataFrame(numpy.arange(9).reshape(3,3), columns=label)\r\n",
    "arr_info([ frame0_2, frame0_3 ])"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 5.5.1 重排分级顺序 Reordering and Sorting Levels\r\n",
    "\r\n",
    "+ `object.swaplevel(\"level_1\", \"Level_2\")`：交换两个级别的层次化索引\r\n",
    "+ 【弃用】`object.sortlevel(\"level_1\", \"Level_2\")`：交换两个级别的层次化索引"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# swaplevel()\r\n",
    "\r\n",
    "frame1_1 = pandas.DataFrame(numpy.arange(18).reshape(6,3), \r\n",
    "                            index=[[\"A\",\"A\",\"A\",\"A\",\"B\",\"B\"], [1,2,3,4,1,2]], \r\n",
    "                            columns=[[\"item_1\",\"item_1\",\"item_2\"], [\"Red\",\"Green\",\"Blue\"]])\r\n",
    "frame1_1.index.names = [\"Group\", \"NO.\"]\r\n",
    "frame1_1.columns.names = [\"Item\", \"Color\"]\r\n",
    "\r\n",
    "arr_info([ frame1_1 ])\r\n",
    "arr_info([ frame1_1.swaplevel(\"Group\", \"NO.\") ])\r\n",
    "# Series同理"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 5.5.2 根据级别汇总统计 Summary Statistics by Levels\r\n",
    "\r\n",
    "+ `object.groupby(level=\"XXX\").sum()`"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "source": [
    "# 按级别汇总统计\r\n",
    "\r\n",
    "frame1_1 = pandas.DataFrame(numpy.arange(18).reshape(6,3), \r\n",
    "                            index=[[\"A\",\"A\",\"A\",\"A\",\"B\",\"B\"], [1,2,3,4,1,2]], \r\n",
    "                            columns=[[\"item_1\",\"item_1\",\"item_2\"], [\"Red\",\"Green\",\"Blue\"]])\r\n",
    "frame1_1.index.names = [\"Group\", \"NO.\"]\r\n",
    "frame1_1.columns.names = [\"Item\", \"Color\"]\r\n",
    "\r\n",
    "arr_info([ frame1_1 ])\r\n",
    "arr_info([ frame1_1.sum(level=\"Group\") ])   # 按Group分类，对列进行操作（A的列进行求和，B的列进行求和）\r\n",
    "arr_info([ frame1_1.sum(level=\"Item\", axis=1) ])  # 按Item分类，对行进行操作（item_1的行求和，item_2的行求和，而非本行所有元素求和）\r\n",
    "# 【FutureWarning】上述写法将被弃用，改用：frame1_1.groupby(level=\"Group\").sum()"
   ],
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "【1】\n",
      " 类 型: <class 'pandas.core.frame.DataFrame'>\n",
      "Item      item_1       item_2\n",
      "Color        Red Green   Blue\n",
      "Group NO.                    \n",
      "A     1        0     1      2\n",
      "      2        3     4      5\n",
      "      3        6     7      8\n",
      "      4        9    10     11\n",
      "B     1       12    13     14\n",
      "      2       15    16     17\n",
      "【1】\n",
      " 类 型: <class 'pandas.core.frame.DataFrame'>\n",
      "Item  item_1       item_2\n",
      "Color    Red Green   Blue\n",
      "Group                    \n",
      "A         18    22     26\n",
      "B         27    29     31\n",
      "【1】\n",
      " 类 型: <class 'pandas.core.frame.DataFrame'>\n",
      "Item       item_1  item_2\n",
      "Group NO.                \n",
      "A     1         1       2\n",
      "      2         7       5\n",
      "      3        13       8\n",
      "      4        19      11\n",
      "B     1        25      14\n",
      "      2        31      17\n"
     ]
    },
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "C:\\Users\\WWC\\AppData\\Local\\Temp/ipykernel_13732/44669927.py:10: FutureWarning: Using the level keyword in DataFrame and Series aggregations is deprecated and will be removed in a future version. Use groupby instead. df.sum(level=1) should use df.groupby(level=1).sum().\n",
      "  arr_info([ frame1_1.sum(level=\"Group\") ])\n",
      "C:\\Users\\WWC\\AppData\\Local\\Temp/ipykernel_13732/44669927.py:11: FutureWarning: Using the level keyword in DataFrame and Series aggregations is deprecated and will be removed in a future version. Use groupby instead. df.sum(level=1) should use df.groupby(level=1).sum().\n",
      "  arr_info([ frame1_1.sum(level=\"Item\", axis=1) ])  # 按Item分类，对行进行操作（item_1的行求和，item_2的行求和，而非本行所有元素求和）\n"
     ]
    }
   ],
   "metadata": {}
  },
  {
   "cell_type": "markdown",
   "source": [
    "### 5.5.3 使用DataFrame的列 Using a DataFrame's Columns\r\n",
    "\r\n",
    "+ `.set_index()`\r\n",
    "+ `.reset_index()`"
   ],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 将某一列数据设置为index\r\n",
    "\r\n",
    "frame3_1 = pandas.DataFrame({\"A\": range(1, 8), \"B\": range(7, 0, -1),\r\n",
    "                            \"C\": [\"one\", \"one\", \"one\", \"two\", \"two\", \"two\", \"two\",],\r\n",
    "                            \"D\": [1, 2, 3, 1, 2, 3, 4 ]})\r\n",
    "\r\n",
    "arr_info([ frame3_1 ])\r\n",
    "arr_info([ frame3_1.set_index(\"A\") ])\r\n",
    "arr_info([ frame3_1.set_index([\"C\", \"D\"]) ])        # 传入列表，构建层次化索引\r\n",
    "arr_info([ frame3_1.set_index(\"A\", drop=False) ])   # drop参数表示是否丢弃设定为index的轴，默认为True"
   ],
   "outputs": [],
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "source": [
    "# 将层次化的索引重排为非层次化的，相当于.set_index()的反向操作\r\n",
    "\r\n",
    "arr_info([ frame3_1.reset_index() ])"
   ],
   "outputs": [],
   "metadata": {}
  }
 ],
 "metadata": {
  "orig_nbformat": 4,
  "language_info": {
   "name": "python",
   "version": "3.9.6",
   "mimetype": "text/x-python",
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "pygments_lexer": "ipython3",
   "nbconvert_exporter": "python",
   "file_extension": ".py"
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.9.6 64-bit ('venv': venv)"
  },
  "interpreter": {
   "hash": "2df30c634058628fc5df5036be3dee25b811a252316c0aa1ff7f50eb8aecb5be"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}